#include "t_box_player_ctx.h"
////#include "t_box_video.h"

namespace BoxPlayer //[class T_Box_Context_Content]
{
    class T_Box_Context_Content : public T_Box_Context
    {
    public:
        explicit T_Box_Context_Content(T_Box_Core *a_core)
            : T_Box_Context(a_core)
        {
            this->openAudio();
            this->openVideo();
            this->core()->seekManager()->addSeekable(this);
        }
    protected:
        void run();
        ////bool handleSeek();
    public:
        bool needBuffering();
    };
};

namespace BoxPlayer //[class T_Box_Player_Context]
{
    class T_Box_Player_ContextPrivate : public QObject
    {
    protected:
        Q_DECLARE_PUBLIC(T_Box_Player_Context)
        T_Box_Player_Context *q_ptr;
    public:
        T_Box_Context_Content work;
        //T_Box_Context video1;
    public:
        explicit T_Box_Player_ContextPrivate(T_Box_Player_Context *me)
            : QObject(me), q_ptr(me)
            , work(me->m_core)
        {
        }
        virtual ~T_Box_Player_ContextPrivate()
        {
        }
    };

    T_Box_Player_Context::T_Box_Player_Context(T_Box_Core *a_core)
        : m_core(a_core)
        , m_speaker(NULL)
        , m_decode_thread(0)
        , m_audio_thread(0)
    {
        d_ptr = new T_Box_Player_ContextPrivate(this);
        Q_D(T_Box_Player_Context);
        Q_ASSERT(m_core);
        Q_ASSERT(m_core->isValid());
        Q_ASSERT(m_core->globalControl()->isValid());
        Q_ASSERT(m_core->globalControl()->formatContext());
        Q_ASSERT(d->work.video()->isOpen());
        Q_ASSERT(d->work.audio()->isOpen());
        d->work.start();

        //d->video1.openVideo();
        //m_core->seekManager()->addSeekable(&d->video1);
#if !USE_AUDIO_THREAD
        m_speaker = new T_Box_AudioOutput(&d->work, 0);
        m_speaker->start();
#endif
    }

    T_Box_Player_Context::~T_Box_Player_Context()
    {
        Q_D(T_Box_Player_Context);
        qDebug() << "[~T_Box_Player_Context(1)]";
        m_speaker->stop();
        delete m_speaker;
        qDebug() << "[~T_Box_Player_Context(2)]";
        d->work.finish();
        qDebug() << "[~T_Box_Player_Context(3)]";
    }

    void T_Box_Player_Context::start()
    {
#if USE_AUDIO_THREAD
        Q_ASSERT(!m_audio_thread);
        m_audio_thread = new T_Box_Player_Audio_Thread(this);
        m_audio_thread->start();
#endif
    }
    void T_Box_Player_Context::finish()
    {
        qDebug() << "[T_Box_Player_Context::finish(1)]";
        ////if(m_decode_thread) m_decode_thread->finish();
        qDebug() << "[T_Box_Player_Context::finish(2)]";
        if(m_audio_thread) m_audio_thread->finish();
        qDebug() << "[T_Box_Player_Context::finish(3)]";
    }

    T_Box_Core *T_Box_Player_Context::core()
    {
        return m_core;
    }

    T_Box_Media *T_Box_Player_Context::audio()
    {
        Q_D(T_Box_Player_Context);
        return d->work.audio();
    }

    T_Box_Media *T_Box_Player_Context::video()
    {
        Q_D(T_Box_Player_Context);
        return d->work.video();
    }


    bool T_Box_Player_Context::needBuffering()
    {
        Q_D(T_Box_Player_Context);
        return d->work.needBuffering();
    }

    T_Box_AudioOutput *T_Box_Player_Context::speaker()
    {
        return m_speaker;
    }

    void T_Box_Context_Content::run()
    {
        this->f_ready() = true;
        T_Box_Seek_Manager *v_seek_manager = this->core()->seekManager();
        for(;;)
        {
            if(this->f_closing())
            {
                break;
            }
            {
                T_READ_LOCKER(v_seek_lock, v_seek_manager);
                if(video()->maxTiming()>=v_seek_manager->seekableDuration())
                {
                    v_seek_manager->wait(1);
                    continue;
                }
                QSharedPointer<T_AV_Packet> v_av_packet = this->enqueuePacket();
                if(!v_av_packet)
                {
                    v_seek_manager->wait(1);
                }
            }
        }
    }

    bool T_Box_Context_Content::needBuffering()
    {
#if 0x1
        if(this->core()->seekManager()->isCriticalTime())
        {
            return false;
        }
#endif
        if(this->video()->packetQueue()->endlessDuration(this->core()->timeline()->elapsed())<T_Box::PLAYER_BUFFER_TIME)
        {
            return true;
        }
        return false;
    }
};

namespace BoxPlayer //[class T_Box_Player_Audio_Thread]
{
    void T_Box_Player_Audio_Thread::run()
    {
        m_player_ctx->m_speaker = new T_Box_AudioOutput(&m_player_ctx->d_func()->work, 0);
        m_player_ctx->m_speaker->start();
        this->f_ready() = true;
        exec();
    }
};

#if 0x0
//http://blog.csdn.net/huyinguo/archive/2009/10/25/4725326.aspx
//needs ffmpeg-mt
static void validate_thread_parameters(AVCodecContext *avctx)
{
    int frame_threading_supported = (avctx->codec->capabilities & CODEC_CAP_FRAME_THREADS)
                                    && !(avctx->flags & CODEC_FLAG_TRUNCATED)
                                    && !(avctx->flags & CODEC_FLAG_LOW_DELAY)
                                    && !(avctx->flags2 & CODEC_FLAG2_CHUNKS);
    if (avctx->thread_count <= 1)
        avctx->active_thread_type = 0;
    else if (frame_threading_supported && (avctx->thread_type & FF_THREAD_FRAME))
        avctx->active_thread_type = FF_THREAD_FRAME;
    else
        avctx->active_thread_type = FF_THREAD_SLICE;
}
#endif
